<?php
/**
 * CK 快速开发框架
 * User: Clake
 * Date: 15/6/2
 * Time: 02:05
 */

namespace CK\Core;

use CK\Ex\ExNotFound;
use CK\Util\IO\File;
/**
 * Class Router
 * 路由类
 * @package CK\Core
 */
class Router extends Base {

    /**
     * 初始化的实例
     * @var array
     */
    private static $_instance;

    //路由加载的模块
    private $_module;
    //路由加载的控制器
    private $_controller;
    //路由加载的执行动作
    private $_action;
    //当前路径
    private $_path;
    //当前查询字符串
    private $_query;
    //当前路径分段数组
    private $_params;
    //路由地址勾子
    private static $_hooks = [];
    /**
     * 路由默认配置
     * @var array
     */
    private $_conf = [
        //默认模块
        'def_module' => null,
        //默认控制器
        'def_controller'=>null,
        //默认动作
        'def_action'=>null,
        //动作方法前缀
        'method_prefix'=>'ac',
        //允许执行模块
        'allowed_modules'=>[],
        //URL地址分区配置模板
        'url_split'=>'{module}/{controller}/{action}'
    ];

    /**
     * 实列化当前类
     * @param null $conf
     * @param bool $is_new
     *
     * @return Router
     */
    public static function inst($conf=null,$is_new = false) {
        if ($is_new) {
            return new self($conf);
        } else {
            if (!isset(self::$_instance)) {
                self::$_instance = new self($conf);
            }
            return self::$_instance;
        }
    }

    public function __construct($conf) {
        if (is_array($conf)) {
            $this->_conf = array_merge($this->_conf,$conf);
        } else {
            $def_conf = Config::inst(CK_DEF_CONF)->get('router');
            if (is_array($def_conf)) {
                $this->_conf = array_merge($this->_conf,$def_conf);
            }
        }

        $this->init();
    }

    /**
     * 初始化路由类
     *
     * @throws ExNotFound
     */
    private function init() {
        $this->explainPath();
        $params = [];
        $url_arr = explode('/',$this->_conf['url_split']);
        foreach ($url_arr as $key=>$value) {
            if (isset($this->_params[$key]) && !empty($this->_params[$key])) {
                $value = trim($value,'{}');
                $class = $this->_params[$key];
                if (strpos($class,'_') === false) {
                    $params[$value] = ucfirst(strtolower($class));
                } else {
                    //把有下划线的方法转为陀峰格式
                    $params[$value] = under2hump($class);
                }
            }
        }

        $this->_module = isset($params['module']) ?$params['module']:$this->_conf['def_module'];
        $this->_controller = isset($params['controller']) ?$params['controller']:$this->_conf['def_controller'];
        $this->_action = isset($params['action']) ?$params['action']:$this->_conf['def_action'];

    }

    /**
     * 解释PATH_INFO到路由
     */
    protected function explainPath() {
        if (CK_IS_CLI) {
            $argv = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
            $argv = explode('?',$argv,1);
            $this->_path = trim($argv[0],'/');
            $this->_query = $argv[1];
        } else {
            $fields = ['REDIRECT_PATH_INFO','ORIG_PATH_INFO','PATH_INFO'];
            foreach ($fields as $field) {
                if (isset($_SERVER[$field])) {
                    $this->_path = trim($_SERVER[$field],'/');
                    break;
                }
            }
        }

        $this->_params = explode('/',$this->_path);
    }

    /**
     * 检查模块,控制,动作类是否存在
     * @param        $class
     * @param string $type module|controller|action
     *
     * @return bool
     */
    protected function checkExists($class,$type='module') {
        $flag = false;
        switch ($type) {
            case 'module':
                !File::Exists(APP_LIB_PATH.CK_TOP_NS.'/'.$class) or $flag = true;
                break;
            case 'controller':
                !File::Exists(APP_LIB_PATH.CK_TOP_NS.'/'.$this->_module.'/Controller/'.$class.'.php') or $flag = true;
                break;
            case 'action':
                $path = CK_TOP_NS.'\\'.$this->_module.'\\Controller\\'.$this->_controller;
                if (method_exists($path,$this->_conf['method_prefix'].$class)) $flag = true;
                break;
        }
        return $flag;
    }

    /**
     * 得到当前URL路由的模块类
     * @return Module
     * @throws ExNotFound
     */
    public function getModule() {
        if (!in_array($this->_module,$this->_conf['allowed_modules'])) {
            throw new ExNotFound('deny this Module::'.$this->_module);
        }

        if (empty($this->_module) || !$this->checkExists($this->_module)) {
            throw new ExNotFound('Not found this Module::'.$this->_module);
        }
        $class = CK_TOP_NS.'\\'.$this->_module.'\\Module';
        return $class::inst();
    }

    /**
     * 得到当前URL路由的控制器类
     * @return Controller
     * @throws ExNotFound
     */
    public function getController() {
        if (empty($this->_controller) || !$this->checkExists($this->_controller,'controller')) {
            throw new ExNotFound('Not found this Controller::'.$this->_controller);
        }
        $class = CK_TOP_NS.'\\'.$this->_module.'\\Controller\\'.$this->_controller;
        return $class::inst();
    }

    /**
     * 得到当前URL路由的执行动作
     * @return string
     * @throws ExNotFound
     */
    public function getAction() {
        if (empty($this->_action) || !$this->checkExists($this->_action,'action')) {
            throw new ExNotFound('Not found this Action::'.$this->_conf['method_prefix'].$this->_action);
        }
        return $this->_conf['method_prefix'].$this->_action;
    }

    /**
     * 得到当前URL地址分割属性值
     * @param $key
     *
     * @return null
     */
    public function getUrlParams($key=null) {
        if ($key !== null && isset($this->_params[$key])) {
            return $this->_params[$key];
        } else {
            return $this->_params;
        }
    }

    /**
     * 得到当前路由属性
     * @param $type 'module|controller|action'
     *
     * @return string
     */
    public function getRouterParam($type) {
        switch ($type) {
            case "module":
                return $this->_module;
            case "controller":
                return $this->_controller;
            case "action";
                return $this->_action;
        }
    }

    /**
     * 得到当前URL访问地址
     * @param bool|false $full_path 是否返回全路径
     *
     * @return mixed
     */
    public function getCurrentUrl($full_path=false) {
        if ($full_path) {
            $path = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        } else {
            $path = $_SERVER['REQUEST_URI'];
        }
        return $path;
    }

    public static function Hook($path,$callback) {
        $hook = [
            'path'=>$path,
            'callback'=>$callback
        ];
        self::$_hooks[] = $hook;
    }
}